﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using LaughingDog.GameObjects;
using LaughingDog.Components;

namespace LaughingDog.Managers
{
    public sealed class GameObjectManager : DrawableGameComponent
    {
        #region Private Members

        private SpriteBatch mSpriteBatch;
        private List<GameObject> mObjects = new List<GameObject>();
        private List<Sprite> mSprites = new List<Sprite>();                

        #endregion

        #region Public Properties

        /// <summary>
        /// A list of all currently managed GameObjects.
        /// </summary>
        public List<GameObject> Objects { get { return mObjects; } }

        /// <summary>
        /// Gets the current amount of managed GameObjects of all types.
        /// </summary>
        public Int32 Count { get { return mObjects.Count; } }

        #endregion

        #region Singleton

        static readonly GameObjectManager instance = new GameObjectManager(Engine.Game);

        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static GameObjectManager()
        {
        }

        GameObjectManager(Game game)
            : base(game)
        {
        }

        public static GameObjectManager Instance
        {
            get
            {
                return instance;
            }
        }

        #endregion

        #region Protected Methods

        protected override void LoadContent()
        {
            mSpriteBatch = new SpriteBatch(GraphicsDevice);

            base.LoadContent();
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Updates the GameObjectManager.
        /// </summary>
        /// <param name="gameTime">A snapshot of the current game time.</param>
        public override void Update(GameTime gameTime)
        {
            foreach (GameObject gameObject in new List<GameObject>(mObjects))
            {
                if (gameObject.Active)
                {
                    gameObject.Update();

                    //foreach (GameObject otherGameObject in new List<GameObject>(mObjects))
                    //{
                    //    if (gameObject != otherGameObject) // Don't check if both objects are the same.
                    //    {
                    //        if (gameObject.Renderer != null && otherGameObject.Renderer != null) // Don't check if any of the GameObjects don't have Renderer components.
                    //        {
                    //            if (gameObject.Renderer.Visible && otherGameObject.Renderer.Visible) // Only check if both Renderers are visible.
                    //            {
                    //                if (gameObject.Active && otherGameObject.Active) // Only check if both GameObjects are active.
                    //                {
                    //                    if (gameObject.Transform.BoundingBox.Intersects(otherGameObject.Transform.BoundingBox)) // First check for a basic bounding box collision.
                    //                    {
                    //                        gameObject.BoundingBoxCollision(otherGameObject);
                    //                        if (gameObject.OnBoundingBoxCollision != null) gameObject.OnBoundingBoxCollision(otherGameObject);

                    //                        if (Renderer.IntersectPixelsTranslated(gameObject, otherGameObject))
                    //                        {
                    //                            gameObject.PixelPerfectCollisionTranslated(otherGameObject);
                    //                            if (gameObject.OnPixelPerfectCollision != null) gameObject.OnPixelPerfectCollision(otherGameObject);
                    //                        }
                    //                        if (Renderer.IntersectPixels(gameObject, otherGameObject))
                    //                        {
                    //                            gameObject.PixelPerfectCollision(otherGameObject);
                    //                            if (gameObject.OnPixelPerfectCollision != null) gameObject.OnPixelPerfectCollision(otherGameObject);
                    //                        }
                    //                    }
                    //                }
                    //            }
                    //        }
                    //    }                        
                    //}
                }
            }

            base.Update(gameTime);
        }

        /// <summary>
        /// Draws the GameObjectManager.
        /// </summary>
        /// <param name="gameTime">A snapshot of the current game time.</param>
        public override void Draw(GameTime gameTime)
        {
            Engine.GraphicsDeviceManager.GraphicsDevice.Clear(Screen.BackgroundColour);

            if(Camera.ActiveCamera == null) // No camera so just draw normally
                mSpriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.FrontToBack, SaveStateMode.SaveState);
            else
                mSpriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.FrontToBack, SaveStateMode.SaveState, Camera.ActiveCamera.TransformationMatrix);
 

            foreach (GameObject gameObject in new List<GameObject>(mObjects))
            {
                if (gameObject.Active)
                    gameObject.Draw(mSpriteBatch);
            }

            mSpriteBatch.End();

            base.Draw(gameTime);
        }

        /// <summary>
        /// Adds a GameObject to the manager.
        /// </summary>
        /// <typeparam name="T">The type of GameObject to add.</typeparam>
        /// <returns>The added GameObject.</returns>
        public T AddGameObject<T>() where T : GameObject
        {
            object obj = Activator.CreateInstance(typeof(T));

            mObjects.Add((T)obj);

            if (obj is Sprite)
                mSprites.Add((Sprite)obj);  

            return (T)obj;
        }

        /// <summary>
        /// Adds a GameObject to the manager.
        /// </summary>
        /// <param name="gameObject">The GameObject to add.</param>
        /// <returns>The added GameObject.</returns>
        public GameObject AddGameObject(GameObject gameObject)
        {
            mObjects.Add(gameObject);

            return gameObject;
        }

        /// <summary>
        /// Removes a GameObject from the manager.
        /// </summary>
        /// <param name="gameObject">The GameObject to remove.</param>
        public void RemoveGameObject(GameObject gameObject)
        {
            if (gameObject is Sprite)
                mSprites.Remove((Sprite)gameObject); 

            mObjects.Remove(gameObject);
        }

        /// <summary>
        /// Returns a count of how many of the specified type of GameObject are currently being managed.
        /// </summary>
        /// <typeparam name="T">The type of GameObject.</typeparam>
        /// <returns>The object count.</returns>
        public Int32 GetObjectCount<T>()
        {
            Int32 count = 0;

            foreach (GameObject gameObject in new List<GameObject>(mObjects))
                if (gameObject is T)
                    count++;

            return count;
        }

        #endregion
    }
}
